package com.xprophet.crs.jpeg.codec.impl;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.xprophet.crs.jpeg.codec.AbstractJPEGDecoder;
import com.xprophet.crs.jpeg.codec.FingerprintType;
import com.xprophet.crs.jpeg.codec.IJPEGDecoder;
import com.xprophet.crs.jpeg.codec.model.Fingerprint;

public class XDecoderImpl extends AbstractJPEGDecoder implements IJPEGDecoder {
	
	static Logger log = Logger.getLogger(SnoopDecoderImpl.class);
	
	public static final String ID = "xprophet";
	
	private static final Map<String, List<Fingerprint>> matchMap = new HashMap<String, List<Fingerprint>>();//Collections.unmodifiableMap(new HashMap<String, List<Fingerprint>>());
	
	public XDecoderImpl() {
		
	}

	public XDecoderImpl(List<Fingerprint> list) {
		updateMatchMap(list);
	}
	
	private void updateMatchMap(List<Fingerprint> list) {
		if (list != null) {
			for (Fingerprint fp : list) {
				updateMatchMap(fp);
			}
		}
	}
	
	public String getData() {
		if (info == null) {
			return null;
		}
		int[] cmatrix = info.getDQT0();
		int[] imatrix = info.getDQT1();
		if (cmatrix == null || imatrix == null) {
			return null;
		}
		
		StringBuilder buf = new StringBuilder("Xprophet").append(":");
		for (int i = 0; i <= QTABLE_MAX_IDX; i++) {
			buf.append(cmatrix[i]);
			if (i < QTABLE_MAX_IDX)
				buf.append(",");
		}
		buf.append(";");
		for (int i = 0; i <= QTABLE_MAX_IDX; i++) {
			buf.append(imatrix[i]);
			if (i < QTABLE_MAX_IDX)
				buf.append(",");
		}
		info.setDQTString(buf.toString());
		return info.getDQTString();
	}

	public String getEncryptedData() {
		this.getData();
		if (info == null || info.getDQTString() == null) {
			return "";
		}
		
		MessageDigest messageDigest = null;
		try {
			messageDigest = MessageDigest.getInstance("MD5");
			messageDigest.reset();
			messageDigest.update(info.getDQTString().getBytes("UTF-8"));

		} catch (NoSuchAlgorithmException e) {
			log.error("", e);
			return "";
		} catch (UnsupportedEncodingException e) {
			log.error("", e);
			return "";
		}

		byte[] byteArray = messageDigest.digest();
		StringBuilder md5StrBuff = new StringBuilder();
		for (int i = 1 /* omit index 0 */; i < byteArray.length; i++) {
			String tmpStr = Integer.toHexString(0xFF & byteArray[i]);
			if (tmpStr.length() == 1) {
				md5StrBuff.append("0");
			}
			md5StrBuff.append(tmpStr);
		}

		Fingerprint fp = info.getFingerprint() == null ? new Fingerprint() : info.getFingerprint();
		fp.setX_sig(md5StrBuff.toString().toUpperCase());
		info.setFingerprint(fp);
		return fp.getX_sig();
	}
	
	public Fingerprint getMatchResult(String key, Fingerprint lastResult) {
		Fingerprint retval = lastResult;
		if (retval == null) {
			retval = new Fingerprint();
		}
		retval.setX_sig(key);
		
		String make = exif.getMake();
		String model = exif.getModel();
		String editor = exif.getEditor();
		retval.setMake(make);
		retval.setModel(model);
		retval.setEditor(editor);

		List<Fingerprint> fps = matchMap.get(key);
		if (fps == null) {
			return retval;
		} else {
			for (Fingerprint fp : fps) {
				if (fp.getType() == FingerprintType.HARDWARE) {
					if (fp.getMake().equalsIgnoreCase(make) &&
							fp.getModel().equalsIgnoreCase(model)) {
						fp.cloneHardware(retval);
						return retval;
					}
				} else if (fp.getType() == FingerprintType.SOFTWARE) {
					if (fp.getMake().equalsIgnoreCase(editor)) {
						fp.cloneSoftware(retval);
						return retval;
					}
				}
			}
		}
		return retval;
	}

	public String getID() {
		return XDecoderImpl.ID;
	}
	
	public void updateMatchMap(Fingerprint newValue) {
		String sig = newValue.getX_sig();
		List<Fingerprint> value = matchMap.get(sig);
		if (value == null) {
			value = new LinkedList<Fingerprint>();
			value.add(newValue);
			matchMap.put(sig, value);
		} else {
			value.add(newValue);
		}
	}
}
